package graphics.primitive;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;

import mathematics.Matrix4x4;
import mathematics.geometry.Geometry;

import colors.Color;
import graphics.engine.*;

import utils.Point3D;
import utils.Vector3D;

public abstract class BasePrimitive
{	
	protected VertexBufferObject dynamicVbo;
	
	protected boolean isAlwaysFacingCamera = false;

	private Point3D Position;
	private Vector3D Scale;
	private Vector3D Orientation;
	
	public Geometry geometry;
	
	private Color color;
		
	public float xMin = Float.MAX_VALUE;
	public float xMax = Float.MIN_VALUE;
	public float yMin = Float.MAX_VALUE;
	public float yMax = Float.MIN_VALUE;

	private Integer texture;

	public BasePrimitive(GL gl)
	{
		this.setColor(new Color(0.0f, 0.0f, 0.0f));
		this.setPosition(new Point3D(0,0,0));
		this.setScale(new Vector3D(1,1,1));
	}
	
	public abstract void GenerateVertices(GL gl);
	public abstract void GenerateIndices(GL gl);	

	public float[] WorldMatrix()
	{
		Matrix4x4 worldMatrix = new Matrix4x4();
		
		Matrix4x4 translation = new Matrix4x4();
		translation.setToTranslation(new Vector3D(this.getPosition().x(), this.getPosition().y(), this.getPosition().z()));
		
		Matrix4x4 scale = new Matrix4x4();
		scale.setToScale(this.getScale());

		worldMatrix = Matrix4x4.mult(scale, worldMatrix);
		
		worldMatrix = Matrix4x4.mult(translation, worldMatrix);

		return worldMatrix.toFloatArray();
	}
	
	public void Draw(GLAutoDrawable drawable, ProgramShader programShader)
	{
		GL gl = drawable.getGL();
		VertexBufferObject vbo;
		int beginMode;
		if(this.getClass() == Cube.class)
		{
			beginMode = Cube.beginMode;
			vbo = Cube.vbo;
		}
		else if(this.getClass() == Quad.class)
		{
			beginMode = Quad.beginMode;
			vbo = Quad.vbos.get(gl.hashCode());
		}
		else  if(this.getClass() == Disk.class)
		{
			beginMode = Disk.beginMode;
			vbo = Disk.vbos.get(gl.hashCode());
		}		
		else  if(this.getClass() == Circle.class)
		{
			beginMode = Circle.beginMode;
			gl.glLineWidth(((Circle)this).lineWidth);
			vbo = Circle.vbos.get(gl.hashCode());
		}
		else if(this.getClass() == Polyline.class)
		{
			beginMode = GL.GL_LINE_STRIP;
			gl.glLineWidth(((Polyline)this).lineWidth);
			vbo = this.dynamicVbo;
		}
		else
		{
			beginMode = GL.GL_POLYGON;
			vbo = this.dynamicVbo;			
		}

		gl.glUniformMatrix4fv(programShader.WorldMatrixUniformID, 1, false, this.WorldMatrix(), 0);		
			
		gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
		gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo.VertexBufferID);
		gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
		
		gl.glColor4f(this.getColor().R, this.getColor().G, this.getColor().B, this.getColor().A);

		gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vbo.IndexBufferID);
		if(this.texture != null)
		{
			gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
			gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo.TextureCoordBufferID);
			gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);
			
			gl.glEnable(GL.GL_TEXTURE_2D);
			gl.glUniform1f( programShader.isTextured, 1.0f);
			gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
			gl.glUniform1f(programShader.TextureUniformID, texture);
		}
		else
		{
			gl.glUniform1f(programShader.isTextured, 0);
		}
		gl.glDrawElements(beginMode, vbo.indexCount, GL.GL_UNSIGNED_SHORT, 0);

		gl.glDisable(GL.GL_TEXTURE_2D);

		gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
			
	}

	public Point3D getPosition() {
		return Position;
	}

	public void setPosition(Point3D position) {
		Position = position;
	}

	public Vector3D getScale() {
		return Scale;
	}

	public void setScale(Vector3D scale) {
		Scale = scale;
	}

	public Color getColor() {
		return color;
	}

	public void setColor(Color color) {
		this.color = color;
	}

	public Integer getTexture() {
		return texture;
	}

	public void setTexture(Integer texture) {
		this.texture = texture;
	}
}